home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Info-Mac 3
/
Info_Mac_1994-01.iso
/
Development
/
Information
/
Mac Programming Secrets 1.0.1
/
Chapter 05
/
Neat Stuff.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-05-19
|
18KB
|
588 lines
#include "Neat Stuff.h"
#include "StdLib.h" // for abs()
#include "GestaltEqu.h"
#include "QDOffscreen.h"
#define NIL 0L
const short kWindowID = 128;
const short kDialogID = 130;
GDHandle pOldDevice;
CGrafPtr pOldPort;
/*******************************************************************************
DoNewWindow
Simply creates a new window and attaches a randomly created QuickDraw
picture to it to handle the updates.
*******************************************************************************/
void DoNewWindow(void)
{
WindowPtr roomWithAView;
PicHandle picturePerfect;
roomWithAView = GetNewWindow(kWindowID, NIL, (WindowPtr) -1);
if (roomWithAView != NIL) {
SetPort(roomWithAView);
picturePerfect = GetRandomPicture(&(roomWithAView->portRect));
SetWindowPic(roomWithAView, picturePerfect);
ShowWindow(roomWithAView);
}
}
/*******************************************************************************
GetRandomPicture
Creates a random picture that fits within the bounds given us. First, we
generate a random number between 40 and 60; this is the number of shapes
we’ll stick in the picture. Next, for each shape, we generate 2 random
coordinates within our limiting bounds. After that, we choose a random
shape to create, and use our two points to create it. When we’ve created
all of our shapes, we close out the picture and return it to the caller.
*******************************************************************************/
PicHandle GetRandomPicture(Rect* bounds)
{
short width;
short height;
PicHandle pitcher;
int numberOfShapes;
int loopy;
Point p1;
Point p2;
Point penSize;
Rect randomRect;
int shapeKind;
qd.randSeed = TickCount();
width = bounds->right - bounds->left;
height = bounds->bottom - bounds->top;
pitcher = OpenPicture(bounds);
PenMode(patXor);
numberOfShapes = abs(Random() % 20) + 40; // 40 to 60 shapes
for (loopy = 0; loopy < numberOfShapes; loopy++) {
p1.h = abs(Random() % width) + bounds->left;
p1.v = abs(Random() % height) + bounds->top;
p2.h = abs(Random() % width) + bounds->left;
p2.v = abs(Random() % height) + bounds->top;
penSize.h = abs(Random() % 10) + 1;
penSize.v = abs(Random() % 10) + 1;
PenSize(penSize.h, penSize.v);
Pt2Rect(p1, p2, &randomRect);
shapeKind = abs(Random() % 4);
switch (shapeKind) {
case 0:
MoveTo(p1.h, p1.v);
LineTo(p2.h, p2.v);
break;
case 1:
FrameRect(&randomRect);
break;
case 2:
FrameOval(&randomRect);
break;
case 3:
FrameRoundRect(&randomRect, 16, 16);
break;
}
}
ClosePicture();
return pitcher;
}
/*******************************************************************************
ShowDialog
Shows a simple modal dialog. Nothing fancy; it’s used as a to contrast to
ShowSavingDialog, which saves the background first and later restores it.
*******************************************************************************/
void ShowDialog(void)
{
DialogPtr dlg;
short item;
dlg = GetNewDialog(kDialogID, NIL, (WindowPtr) -1);
SetPort(dlg);
ShowWindow(dlg);
do {
ModalDialog(NIL, &item);
} while ((item != ok) && (item != cancel));
DisposeDialog(dlg);
}
/*******************************************************************************
ShowSavingDialog
This is the same as ShowDialog, except that it saves the background before
showing the window, and restores the background after the dialog has been
put away.
*******************************************************************************/
void ShowSavingDialog(void)
{
DialogPtr dlg;
short item;
SavedBGHandle savedData;
dlg = GetNewDialog(kDialogID, NIL, (WindowPtr) -1);
SaveBackground(dlg, &savedData);
SetPort(dlg);
ShowWindow(dlg);
do {
ModalDialog(NIL, &item);
} while ((item != ok) && (item != cancel));
DisposeDialog(dlg);
RestoreBackground(savedData);
}
/*******************************************************************************
SaveBackground
Given an invisible window that we’d like to show, this function determines
what parts of our background windows it will cover, and saves those bits
in an offscreen buffer. Information pertaining to the area of the saved
background, as well as the saved bits themselves, is returned in the
SavedBGHandle and passed back to the caller. This handle is later passed
to RestoreBackground to refresh the screen.
*******************************************************************************/
void SaveBackground(WindowPtr w, SavedBGHandle* savedData)
{
RgnHandle coveredWagon;
Rect coveredRect;
GrafPtr offWorld;
GrafPtr desktopPort;
coveredWagon = GetCoveredArea(w);
if (!EmptyRgn(coveredWagon)) {
coveredRect = (**coveredWagon).rgnBBox;
offWorld = CreateOffWorld(coveredRect);
UseOffWorld(offWorld);
desktopPort = GetDesktopPort();
CopyBits(&desktopPort->portBits, &offWorld->portBits, &coveredRect, &offWorld->portRect, srcCopy, NIL);
DoneWithOffWorld(offWorld);
*savedData = (SavedBGHandle) NewHandle(sizeof(SavedBGRecord));
(***savedData).coveredArea = coveredWagon;
(***savedData).offWorld = offWorld;
} else {
*savedData = NIL;
}
}
/*******************************************************************************
RestoreBackground
Given the data saved in SaveBackground, restore the screen, being careful
to alter only the contents of _our_ windows and no one else’s.
*******************************************************************************/
void RestoreBackground(SavedBGHandle savedData)
{
RgnHandle coveredWagon;
Rect coveredRect;
GrafPtr offWorld;
GrafPtr desktopPort = GetDesktopPort();
if (savedData != NIL) {
coveredRect = (**(**savedData).coveredArea).rgnBBox;
UseOffWorld((**savedData).offWorld);
SetPort(desktopPort);
ClipRect(&coveredRect);
SetDesktopDevice();
CopyBits(&(**savedData).offWorld->portBits, &desktopPort->portBits,
&(**savedData).offWorld->portRect, &coveredRect,
srcCopy, (**savedData).coveredArea);
DoneWithOffWorld((**savedData).offWorld);
ValidateWindows((**savedData).coveredArea);
DisposeOffWorld((**savedData).offWorld);
DisposeRgn((**savedData).coveredArea);
DisposeHandle((Handle) savedData);
}
}
/*******************************************************************************
GetDesktopPort
If we are running under a Color QuickDraw environment, return the color
Window Manager port. Otherwise, return the B&W Window Manager port.
*******************************************************************************/
GrafPtr GetDesktopPort()
{
OSErr err;
long value;
GrafPtr desktopPort;
err = Gestalt(gestaltQuickdrawVersion, &value);
if ((err == noErr) && (value >= gestalt8BitQD))
GetCWMgrPort((CGrafPtr*) &desktopPort);
else
GetWMgrPort(&desktopPort);
return desktopPort;
}
/*******************************************************************************
SetDesktopDevice
If we are running on a Mac with Color QuickDraw, set the current GDevice
to be the main device. Otherwise, do nothing, as GDevices aren’t
implemented on this machine.
*******************************************************************************/
void SetDesktopDevice()
{
OSErr err;
long value;
err = Gestalt(gestaltQuickdrawVersion, &value);
if ((err == noErr) && (value >= gestalt8BitQD))
SetGDevice(GetMainDevice());
}
/*******************************************************************************
GetCoveredArea
Given an invisible window, determine the areas of all our other windows
that would be obscured by it. Return this area as a QuickDraw region in
global coordinates.
This routine starts by getting the union of the visRgns of all our
windows. As we traverse the windows, we get their visRgns, translate them
into global coordinates, and merge them into one big region. After that,
we find the rectangle the bounds our invisible window, convert it into a
region, too, and intersect it with our big union of visRgns. This
resulting region is returned to the caller.
*******************************************************************************/
RgnHandle GetCoveredArea(WindowPtr w)
{
Rect wRect;
RgnHandle wRgn;
RgnHandle coveredRgn;
WindowPtr thisWindow;
Point windowLocalTopLeft;
Point delta;
coveredRgn = NewRgn();
thisWindow = FrontWindow();
while (thisWindow != NIL) {
windowLocalTopLeft = topLeft(thisWindow->portRect);
delta = GetGlobalTopLeft(thisWindow);
SubPt(windowLocalTopLeft, &delta);
OffsetRgn(thisWindow->visRgn, delta.h, delta.v);
UnionRgn(thisWindow->visRgn, coveredRgn, coveredRgn);
OffsetRgn(thisWindow->visRgn, -delta.h, -delta.v);
thisWindow = (WindowPtr) ( (WindowPeek) thisWindow)->nextWindow;
}
wRgn = NewRgn();
wRect = GetWindowStructureRect(w);
RectRgn(wRgn, &wRect);
SectRgn(wRgn, coveredRgn, coveredRgn);
DisposeRgn(wRgn);
return coveredRgn;
}
/*******************************************************************************
CreateOffWorld
Given a rectangle, create an offscreen buffer with the same bounds. If we
are running on a Mac with the GWorld routines, use them. Otherwise, as
long as we are running on a Classic QuickDraw machine, whip one up by
hand. In this little sample, we don’t support the creation of color
offscreen buffers on Color QuickDraw machines that don’t have the GWorld
routines.
*******************************************************************************/
GrafPtr CreateOffWorld(Rect globalRect)
{
OSErr err;
long value;
long height;
long width;
short depth;
BitMap bitMap;
GrafPtr oldPort;
GDHandle oldDevice;
GrafPtr newPortBeach;
GWorldPtr braveNewGWorld;
err = Gestalt(gestaltQuickdrawVersion, &value);
if (err == noErr) {
if (value >= gestalt32BitQD12) {
err = NewGWorld(&braveNewGWorld, // result
0, // pixel size (0 = find deepest)
&globalRect, // bounds
NIL, // color table (NIL = default)
NIL, // gDevice (not used in this call)
0); // flags
GetGWorld((CGrafPtr*) &oldPort, &oldDevice);
UseOffWorld((GrafPtr) braveNewGWorld);
EraseRect(&braveNewGWorld->portRect);
DoneWithOffWorld((GrafPtr) braveNewGWorld);
SetGWorld((CGrafPtr) oldPort, oldDevice);
return (GrafPtr) braveNewGWorld;
} else if (value == gestaltOriginalQD) {
height = globalRect.bottom - globalRect.top;
width = globalRect.right - globalRect.left;
bitMap.rowBytes = (((width - 1) / 32) + 1) * 4;
bitMap.bounds = globalRect;
bitMap.baseAddr = (Ptr) NewHandle(height * bitMap.rowBytes);
newPortBeach = (GrafPtr) NewPtr(sizeof(GrafPort));
OpenPort(newPortBeach);
SetPortBits(&bitMap);
newPortBeach->portRect = globalRect;
ClipRect(&globalRect);
CopyRgn(newPortBeach->clipRgn, newPortBeach->visRgn);
GetPort(&oldPort);
SetPort(newPortBeach);
EraseRect(&braveNewGWorld->portRect);
SetPort(oldPort);
return newPortBeach;
} else {
DebugStr("\pSorry, unsupported QuickDraw version.");
}
}
return NIL;
}
/*******************************************************************************
UseOffWorld
This is sort of like a SetPort routine for our offscreen buffers. If we
have created a color offscreen buffer with the GWorld routines, lock down
our pixels and call SetGWorld to set the grafPort and the GDevice. If we
are running on a B&W machine, lock down our bits and simply call SetPort.
In both cases, remember the current GrafPort and (if appropriate) GDevice
so that we can restore them later in DoneWithGWorld.
*******************************************************************************/
void UseOffWorld(GrafPtr offWorlder)
{
if (IsColorPort(offWorlder)) {
GetGWorld(&pOldPort, &pOldDevice);
(void) LockPixels(GetGWorldPixMap((GWorldPtr) offWorlder));
SetGWorld((CGrafPtr) offWorlder, NIL);
} else {
GetPort((GrafPtr*) &pOldPort);
HLock((Handle) offWorlder->portBits.baseAddr);
offWorlder->portBits.baseAddr = *(Handle) (offWorlder->portBits.baseAddr);
SetPort(offWorlder);
}
}
/*******************************************************************************
DoneWithOffWorld
This undoes the effects of UseOffWorld. If we are running with the GWorld
routines, unlock our pixels and reset the GrafPort and GDevice to what
they were before we called UseOffWorld. If we are running on a B&W
machine, unlock our bits and restore the old GrafPort.
*******************************************************************************/
void DoneWithOffWorld(GrafPtr offWorlder)
{
if (IsColorPort(offWorlder)) {
UnlockPixels(GetGWorldPixMap((GWorldPtr) offWorlder));
SetGWorld(pOldPort, pOldDevice);
} else {
offWorlder->portBits.baseAddr = (Ptr) RecoverHandle(offWorlder->portBits.baseAddr);
HUnlock((Handle) offWorlder->portBits.baseAddr);
SetPort((GrafPtr) pOldPort);
}
}
/*******************************************************************************
IsColorPort
Simple utility for determining if the offscreen buffer represents color or
B&W pixels. Since we only create a color buffer if the GWorld routines are
available to us, we often use this utility to see if we can call the
GWorld functions in subsequent routines.
*******************************************************************************/
Boolean IsColorPort(GrafPtr offWorlder)
{
return (offWorlder->portBits.rowBytes & 0x08000) != 0;
}
/*******************************************************************************
DisposeOffWorld
Release the memory for our offscreen buffer. If we created the buffer with
NewGWorld, call DisposeGWorld. If we created the buffer by hand, we
dispose of the two blocks of data we created -- the buffer for the
GrafPort, and the buffer for the bits themselves.
*******************************************************************************/
void DisposeOffWorld(GrafPtr offWorlder)
{
if (IsColorPort(offWorlder)) {
DisposeGWorld((GWorldPtr) offWorlder);
} else {
DisposeHandle((Handle) offWorlder->portBits.baseAddr);
ClosePort(offWorlder);
DisposePtr((Ptr) offWorlder);
}
}
/*******************************************************************************
ValidateWindows
Given a region in global coordinates, validate the parts of all the
windows it intersects. To do this, we traverse all open windows, starting
with FrontWindow(). For each window, we make a temporary copy of
globalRgn, translate that copy into the window’s local coordinate system,
and then call ValidRgn with that copy.
*******************************************************************************/
void ValidateWindows(RgnHandle globalRgn)
{
GrafPtr oldPort;
RgnHandle tempRgn;
WindowPtr thisWindow;
Point windowLocalTopLeft;
Point delta;
GetPort(&oldPort);
tempRgn = NewRgn();
thisWindow = FrontWindow();
while (thisWindow != NIL) {
SetPort(thisWindow);
windowLocalTopLeft = topLeft(thisWindow->portRect);
delta = windowLocalTopLeft;
LocalToGlobal(&delta);
SubPt(windowLocalTopLeft, &delta);
CopyRgn(globalRgn, tempRgn);
OffsetRgn(tempRgn, -delta.h, -delta.v);
SectRgn(thisWindow->visRgn, tempRgn, tempRgn);
ValidRgn(tempRgn);
thisWindow = (WindowPtr) ( (WindowPeek) thisWindow)->nextWindow;
}
DisposeRgn(tempRgn);
SetPort(oldPort);
}
/*******************************************************************************
GetWindowStructureRect
This procedure is used to get the rectangle that surrounds the entire
structure of a window. This works whether or not the window is visible. If
the window is visible, it is a simple matter of using the bounding
rectangle of the structure region. If the window is invisible, the
strucRgn is not valid. To make it valid, the window has to be moved way
off the screen and then made visible. This generates a valid strucRgn,
although it is valid for the position that is way off the screen. It still
needs to be offset back into the original position. Once the bounding
rectangle for the strucRgn is obtained, the window can then be hidden
again and moved back to its correct location. Note that ShowHide is used,
instead of ShowWindow and HideWindow. HideWindow can change the plane of
the window. Also, ShowHide does not affect the hiliting of windows.
*******************************************************************************/
Rect GetWindowStructureRect(WindowPtr window)
{
const short kOffscreenLocation = 0x4000; /* Halfway to infinity */
GrafPtr oldPort;
Rect structureRect;
Point windowLoc;
if (((WindowPeek)window)->visible)
structureRect = (*(((WindowPeek)window)->strucRgn))->rgnBBox;
else {
GetPort(&oldPort);
SetPort(window);
windowLoc = GetGlobalTopLeft(window);
MoveWindow(window, windowLoc.h, kOffscreenLocation, FALSE);
ShowHide(window, TRUE);
structureRect = (*(((WindowPeek) window)->strucRgn))->rgnBBox;
ShowHide(window, FALSE);
MoveWindow(window, windowLoc.h, windowLoc.v, FALSE);
OffsetRect(&structureRect, 0, windowLoc.v - kOffscreenLocation);
SetPort(oldPort);
}
return structureRect;
}
/*******************************************************************************
GetGlobalTopLeft
Return the top left point of the given window’s port in global
coordinates. This returns the top left point of the window’s content area
only; it doesn’t include the window’s drag region (or title bar).
*******************************************************************************/
Point GetGlobalTopLeft(WindowPtr window)
{
GrafPtr oldPort;
Point globalPt;
GetPort(&oldPort);
SetPort(window);
globalPt = topLeft(window->portRect);
LocalToGlobal(&globalPt);
SetPort(oldPort);
return globalPt;
}